
package cn.com.cifi.mars.web.aop;

import java.util.Date;
import java.util.stream.IntStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.ConstraintViolationException;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import cn.com.cifi.mars.common.bean.R;
import cn.com.cifi.mars.common.exception.MsgException;
import cn.com.cifi.mars.constant.CodeEnum;
import cn.com.cifi.mars.constant.Constants;
import cn.com.cifi.mars.constant.LogEventEnum;
import cn.com.cifi.mars.constant.LogSrcEnum;
import cn.com.cifi.mars.constant.LogTypeEnum;
import cn.com.cifi.mars.entity.common.SysLog;
import cn.com.cifi.mars.mapper.mars.SysLogMapper;
import cn.com.cifi.mars.util.UUIDUtils;
import cn.com.cifi.mars.util.WebUtils;
import cn.com.cifi.mars.util.json.JsonResultRespUtils;
import cn.com.cifi.mars.util.json.JsonUtils;
import lombok.extern.slf4j.Slf4j;

/**
 * api 入参出参日志 拦截的异常日志一些与 BaseControllerExceptionHandler 类
 * 相同，原因是希望在产生异常的响应时，将参数与异常的响应也记录。
 * BaseControllerExceptionHandler不能去掉，因为除了此类切入的api的方法外，其他包的方法也需要全局的异常拦截
 */
@Aspect
@Configuration
@Order(1)
@Slf4j
public class ApiLogHandler {

	@Autowired
	private SysLogMapper sysLogMapper;

    /**
     * 切入点
     */
    @Pointcut("execution(* cn.com.cifi.mars.web.action..*.*(..))")
    public void api() {
    }



    /**
     * 环绕
     */
    @Around("api()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {

    	if(null==proceedingJoinPoint){
    		return null;
		}
        // 0：uri，1：参数，2：响应，3：耗时
        Object[] logs = new Object[4];
        long begin = System.currentTimeMillis();
        Object[] logsMsg=new Object[10];
        try {
            Object[] args = proceedingJoinPoint.getArgs();
            logs[1] = args;
            logsMsg=args;
            Object back = proceedingJoinPoint.proceed(args);
            logs[2] = back;
            logs[3] = System.currentTimeMillis() - begin;
            return back;
        } catch (ConstraintViolationException e) {
            // 参数校验异常
            String msg = e.getConstraintViolations().stream().map(x -> x.getMessage()).reduce((x, y) -> x + "," + y)
                    .get();
            return logs[2] = JsonResultRespUtils.create(CodeEnum.ILLEGAL_REQUEST_PARAM_ERROR.getCode(), msg);
        } catch (MethodArgumentNotValidException e) {
            // 对象参数校验异常
            String msg = e.getBindingResult().getAllErrors().stream().map(x -> x.getDefaultMessage())
                    .reduce((x, y) -> x + "," + y).get();
            return logs[2] = JsonResultRespUtils.create(CodeEnum.ILLEGAL_REQUEST_PARAM_ERROR.getCode(), msg);
        } catch (HttpRequestMethodNotSupportedException e) {
            // 请求方法异常
            return logs[2] = JsonResultRespUtils.create(CodeEnum.ILLEGAL_REQUEST_METHOD_ERROR.getCode(),
                    e.getMessage());
        } catch (MsgException e) {
            // 业务异常
            return logs[2] = JsonResultRespUtils.create(e.getCode(), e.getMsg());
        } catch (Throwable throwable) {
            // 其他异常/错误
            log.error(throwable.getMessage(), throwable);
            StackTraceElement[] stackTraceElement=throwable.getStackTrace();
            String row=stackTraceElement[0].toString();
            //记录到数据库
        	SysLog lo = new SysLog();
    		// 输入 输出
        	lo.setSource(LogSrcEnum.PC.getValue());
        	lo.setEvent(LogEventEnum.ADD.getValue());
        	lo.setUseraccount(Constants.SYS);
        	lo.setUrl("ApiLogHandler/around");
        	lo.setCreateTime(new Date());
        	lo.setIp(WebUtils.getLocalIP());
        	lo.setId(UUIDUtils.create());
        	lo.setModule("异常日志");
        	lo.setTitle("后台报错信息");
    		String str1=throwable.getMessage();
    		String str2=JsonResultRespUtils.internalError().getMessage();
    		String str3=JsonResultRespUtils.internalError().getCode()+"";
    		int k=0;
    		String desc="";
    		for (Object object : logsMsg) {
    			k++;
				desc+="参数"+k+":"+object+" ;";
			}

    		Signature signature=proceedingJoinPoint.getSignature();
    		String declaringTypeName=signature.getDeclaringTypeName();
    		String name=signature.getName();
    		desc+="控制层:"+declaringTypeName+" 服务层方法:"+name;
    		String content="错误信息1:"+str1+"; 错误信息2:"+str2+"; 报错编码:"+str3+"; 请求参数信息:"+desc+"; 报错行数:"+row;
    		lo.setContent(content);
    		lo.setType(LogTypeEnum.ONE.getKey());
    		sysLogMapper.insertSelective(lo);
            return logs[2] = JsonResultRespUtils.internalError();
        } finally {
			try {
				ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
						.getRequestAttributes();
				if (null != requestAttributes && !requestAttributes.equals(null)) {
					HttpServletRequest request = requestAttributes.getRequest();
					logs[0] = request.getRequestURI();
					logs[3] = logs[3] == null ? System.currentTimeMillis() - begin : logs[3];
					String uuid = UUIDUtils.create();
					// 过滤参数
					if (logs[1] != null) {
						Object[] args = (Object[]) logs[1];
						IntStream.range(0, args.length).forEach(x -> {
							if (args[x] instanceof HttpServletRequest) {
								args[x] = "HttpServletRequest";
							} else if (args[x] instanceof HttpServletResponse) {
								args[x] = "HttpServletResponse";
							} else if (args[x] instanceof HttpSession) {
								args[x] = "HttpSession";
							}
						});
					}


					String respJson, resp = logs[2] != null
							? (respJson = JsonUtils.convertObjectToJson((R.class.isInstance(logs[2]))||(String.class.isInstance(logs[2]))
									? logs[2]: "")).length() > 1024
	                        ? respJson.substring(0, 1024) + "..."
	                        : respJson
	                        : "无响应";
	                log.info("[{}]接口：{}", uuid, logs[0]);

					if (logs[1] != null) {
						Object[] args = (Object[]) logs[1];
						for (Object o : args) {
							if ((null != o) && (null != o.getClass()) && MultipartFile.class.isInstance(o)) {
								MultipartFile file = (MultipartFile) o;
								log.info("[{}]参数：{}", uuid, "文件 " + file.getOriginalFilename());
							} else {
								log.info("[{}]参数：{}", uuid, JsonUtils.convertObjectToJson(o));
							}
						}
					} else {
						log.info("[{}]参数：{}", uuid, "无参数");
					}

					log.info("[{}]响应：{}", uuid, resp);
					log.info("[{}]耗时：{}", uuid, logs[3] + " ms");
				}

			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
        }
    }

}
